package cn.s1995.sloth.bms.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.s1995.sloth.app.api.entity.AppUser;
import cn.s1995.sloth.bizs.constant.BizCst;
import cn.s1995.sloth.bizs.entity.*;
import cn.s1995.sloth.bizs.enums.NotifyTemplateEnum;
import cn.s1995.sloth.bizs.oo.req.OrderDeliveryREQ;
import cn.s1995.sloth.bizs.oo.vo.OrderItemVO;
import cn.s1995.sloth.bizs.oo.vo.OrderPageVO;
import cn.s1995.sloth.bizs.oo.vo.OrderVO;
import cn.s1995.sloth.bizs.service.*;
import cn.s1995.sloth.common.core.constant.enums.hint.Hint;
import cn.s1995.sloth.common.core.util.R;
import cn.s1995.sloth.common.excel.annotation.ResponseExcel;
import cn.s1995.sloth.common.log.annotation.SysLog;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.http.HttpHeaders;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

/**
 * 订单
 *
 * @author sloth
 * @date 2023-12-15 15:26:19
 */
@RestController
@RequiredArgsConstructor
@RequestMapping("/order")
@Tag(description = "Order", name = "BMS-订单管理")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class OrderController {

    private final OrderService orderService;

    private final OrderItemService orderItemService;

    private final MessageService messageService;

    private final ProdService prodService;

    private final SkuService skuService;

    /**
     * 分页查询
     * @param page 分页对象
     * @param req 订单
     * @return
     */
	@ApiOperationSupport(order = 1)
    @Operation(summary = "分页查询", description = "分页查询")
    @GetMapping("/page")
    @PreAuthorize("@pms.hasPermission('bizs_Order_view')")
    public R<IPage<OrderVO>> getPage(@ParameterObject Page page, @ParameterObject OrderPageVO req) {
		return R.ok(orderService.pageWithOrderItem(page, getQueryWrapper(req)));

    }

    /**
     * 订单列表
     * @param req 订单
     * @return
     */
	@ApiOperationSupport(order = 2)
    @Operation(summary = "订单列表 不分页", description = "订单列表 不分页")
    @GetMapping("/list")
    @PreAuthorize("@pms.hasPermission('bizs_Order_view')")
    public R<List<OrderVO>> getList(@ParameterObject OrderPageVO req) {
		MPJLambdaWrapper<Order> wrapper = getQueryWrapper(req);
		wrapper.selectCollection(OrderItem.class, OrderVO::getOrderItemList, i -> i
					.association(OrderRefund.class, OrderItemVO::getOrderRefund)
					.association(Sku.class, OrderItemVO::getProperties, map -> map.
							result(Sku::getProperties)))
				.leftJoin(OrderItem.class, OrderItem::getOrderId, Order::getId)
				.leftJoin(OrderRefund.class, OrderRefund::getOrderItemId, OrderItem::getId)
				.leftJoin(Sku.class, Sku::getId, OrderItem::getSkuId);
		return R.ok(orderService.selectJoinList(OrderVO.class, wrapper));
    }


    /**
     * 通过id查询订单
     * @param id id
     * @return R
     */
	@ApiOperationSupport(order = 3)
    @Operation(summary = "通过id查询", description = "通过id查询")
    @GetMapping("/{id}")
    @PreAuthorize("@pms.hasPermission('bizs_Order_view')")
    public R getById(@PathVariable("id" ) Long id) {
		Order order = orderService.getById(id);
		if(ObjectUtil.isNotNull(order)) {
			OrderVO orderVo = new OrderVO();
			BeanUtil.copyProperties(order, orderVo, false);
			orderVo.setOrderItemList(orderItemService.selectByOrderId(orderVo.getId()));
			order = orderVo;
		}
        return R.ok(order);
    }

    /**
     * 修改订单优惠价格
     * @return R
     */
	@ApiOperationSupport(order = 4)
    @Operation(summary = "修改订单优惠价格", description = "修改订单价格")
    @SysLog("修改订单价格")
    @PutMapping("/{id}/updateReduceAmount")
    @PreAuthorize("@pms.hasPermission('bizs_Order_edit')")
	@Parameters({
	   @Parameter(name = "id",description = "订单id", required = true, in = ParameterIn.PATH),
	   @Parameter(name = "reduceAmount",description = "优惠金额",required = true, in = ParameterIn.DEFAULT),
	})
    public R updateReduceAmount(@PathVariable Long id, @RequestParam BigDecimal reduceAmount) {
        return orderService.updateReduceAmount(id, reduceAmount);
    }

    /**
     * 关闭订单
     * @param id 订单id
     * @return R
     */
	@ApiOperationSupport(order = 5)
    @Operation(summary = "关闭订单", description = "关闭订单")
    @SysLog("关闭订单")
    @PutMapping("/{id}/close")
    @PreAuthorize("@pms.hasPermission('bizs_Order_edit')")
	@Parameters({
	   @Parameter(name = "id",description = "订单id", required = true, in = ParameterIn.PATH),
	})
    public R close(@PathVariable Long id) {
//        return orderService.closeOrder(id);
		Order order = orderService.getById(id);
		if(ObjectUtil.isNull(order))
			return R.failed(Hint.NOT_FOUND);
		if(BizCst.Order.State.unpay != order.getState())
			return R.failed(Hint.FORBIDDEN);
        List<OrderItem> orderItems = orderItemService.getOrderItemsByOrderId(id);
        order.setOrderItems(orderItems);
        // 取消订单
        orderService.cancelOrders(Collections.singletonList(order));

        // 清除缓存
        for (OrderItem orderItem : orderItems) {
        	prodService.removeProductCacheByProdId(orderItem.getProdId());
            skuService.removeSkuCacheBySkuId(orderItem.getSkuId(), orderItem.getProdId());
        }
		return R.ok();
    }

    /**
     * 订单发货
     * @param id 订单id
     * @return R
     */
	@ApiOperationSupport(order = 6)
    @Operation(summary = "订单发货", description = "订单发货")
    @SysLog("订单发货")
    @PutMapping("/{id}/delivery")
    @PreAuthorize("@pms.hasPermission('bizs_Order_edit')")
    public R delivery(@PathVariable Long id, @RequestBody @Valid OrderDeliveryREQ req) {
		Order order = orderService.getById(id);
		if(ObjectUtil.isNull(order))
			return R.failed(Hint.NOT_FOUND);
		if(BizCst.Order.State.paid != order.getState())
			return R.failed(Hint.FORBIDDEN);
		order.setState(BizCst.Order.State.consignment);
		order.setDeliveryType(req.getDeliveryType());
		order.setLogisticsNo(req.getLogisticsNo());
		order.setDeliveryTime(LocalDateTime.now());
		orderService.updateById(order);
		// 发送消息
        messageService.sendMsg(
        		NotifyTemplateEnum.order_shipped.getId(),
    			0L,
    			order.getUid(),
    			BizCst.Message.Type.order,
    			BizCst.Message.BindType.order,
    			order.getId());
		return R.ok();
    }

    /**
     * 新增订单
     * @param req 订单
     * @return R
     */
	@ApiOperationSupport(order = 10)
    @Operation(summary = "新增订单", description = "新增订单")
    @SysLog("新增订单")
    @PostMapping
    @PreAuthorize("@pms.hasPermission('bizs_Order_add')")
    public R save(@RequestBody Order req) {
        return R.ok(orderService.save(req));
    }

    /**
     * 修改订单
     * @param req 订单
     * @return R
     */
	@ApiOperationSupport(order = 20)
    @Operation(summary = "修改订单", description = "修改订单")
    @SysLog("修改订单")
    @PutMapping
    @PreAuthorize("@pms.hasPermission('bizs_Order_edit')")
    public R updateById(@RequestBody Order req) {
        return R.ok(orderService.updateById(req));
    }

    /**
     * 通过id删除订单
     * @param ids id列表
     * @return R
     */
	@ApiOperationSupport(order = 30)
    @Operation(summary = "通过id删除订单", description = "通过id删除订单")
    @SysLog("通过id删除订单")
    @DeleteMapping
    @PreAuthorize("@pms.hasPermission('bizs_Order_del')")
    public R remove(@RequestBody Long[] ids) {
        return R.ok(orderService.removeBatchByIds(CollUtil.toList(ids)));
    }


    /**
     * 导出excel 表格
     * @param req 查询条件
   	 * @param ids 导出指定ID
     * @return excel 文件流
     */
	@ApiOperationSupport(order = 40)
    @Operation(summary = "导出excel表格", description = "")
    @ResponseExcel
    @GetMapping("/export")
    @PreAuthorize("@pms.hasPermission('bizs_Order_export')")
    public List<Order> export(Order req, Long[] ids) {
        return orderService.list(Wrappers.lambdaQuery(req).in(ArrayUtil.isNotEmpty(ids), Order::getId, ids));
    }

	private static MPJLambdaWrapper<Order> getQueryWrapper(OrderPageVO req) {
		MPJLambdaWrapper<Order> wrapper = new MPJLambdaWrapper<Order>();
		wrapper.selectAll(Order.class)
				.selectAssociation(AppUser.class, OrderVO::getUser)
				.leftJoin(AppUser.class, AppUser::getUserId, Order::getUid)
				.leftJoin(OrderAddress.class, OrderAddress::getId, Order::getOrderAddressId);
        //关键词
        wrapper.and(
                StrUtil.isNotEmpty(req.getSearch()),
                w -> w.like(Order::getProdName, req.getSearch())
                        .or()
                        .like(Order::getId, req.getSearch())
                        .or()
                        .like(Order::getUserNickname, req.getSearch())
                        .or()
                        .like(Order::getShopName, req.getSearch())
        );
		//其他条件
        wrapper.eq(Objects.nonNull(req.getUid()), Order::getUid, req.getUid());
        wrapper.eq(Objects.nonNull(req.getState()), Order::getState, req.getState());
        wrapper.eq(Objects.nonNull(req.getCode()), AppUser::getCode, req.getCode());
        wrapper.eq(Objects.nonNull(req.getCityCode()), OrderAddress::getCityCode, req.getCityCode());
        wrapper.eq(Objects.nonNull(req.getPayMethod()), Order::getPayMethod, req.getPayMethod());
        wrapper.eq(Objects.nonNull(req.getType()), Order::getType, req.getType());
        wrapper.eq(Objects.nonNull(req.getSource()), Order::getSource, req.getSource());
        if(req.hasRangeTime()) {
        	wrapper.between(Order::getCreateTime, req.getRangeTime()[0], req.getRangeTime()[1]);
        }
        wrapper.orderByDesc(Order::getCreateTime);
        return wrapper;
	}
}
